React 生命周期分析

生命周期中有什么比较数据的好的方法吗?

在 componentWillMount、componentDidMount 和 componentWillReceiveProps 三个生命周期中进行 setState 不会引起重新渲染。

shouldComponentUpdate

shouldComponentUpdate 生命周期默认返回 true。

实际效果是每个组件都完成 re-render 和 virtual-DOM diff 过程,虽然组件可能没有变更。react 的性能瓶颈主要表现在:对于 props 和 state 没有变化的组件,react 也要重新生成虚拟 DOM 及虚拟 DOM 的 diff。

shouldComponentUpdate 允许我们手动地判断是否要进行组件更新,根据组件的应用场景设置函数的合理返回值能够帮我们避免不必要的更新

shouldComponentUpdate 这个方法用来判断是否需要调用 render 方法重新描绘 dom。因为 dom 的描绘非常消耗性能,如果我们能在 shouldComponentUpdate 方法中能够写出更优化的 dom diff 算法,可以极大的提高性能

PureComponent

在 React 15.3.0 增加的一个组件基类:React.PureComponent。

import React, { PureComponent } from "react";
class Example extends PureComponent {
  render() {
    // ...
  }
}

它内部的 shouldComponentUpdate 方法都是浅比较 props 和 state 对象的,即只比较对象的第一层的属性及其值是不是相同。

React 组件的生命周期

  • 装载过程(Mount), 也就是把组件第一个在 DOM 树种进行 渲染的过程。
  • 更新过程(Update), 当组件被重新渲染的过程。
  • 卸载过程(Unmount), 组件从 DOM 中删除的过程。

装载

组件第一次被渲染的时候,一次调用的函数是:

  • constructor

  • getInitialState

  • componentWillMount

    render 函数调用之前调用。执行这个函数时,还没有任何渲染的结果,即使调用this.setState修改状态也不会引发重新绘制。在 componentWillMount 函数中做的事情,都可以提前到 constructor 中去做。

  • render

    所有 React 组件的父类React.Component类对除了 render 函数之外的生命周期函数都有默认实现。需要注意的是,render 函数是一个纯函数,完全根据this.statethis.props来决定返回的结果,而且不要产生任何副作用。

  • componentDidMount

    render 函数调用之后调用。需要注意的是,render 函数被调用之后,componentDidMount 函数并不会立即被调用,componentDidMount 函数被调用的时候,render 函数返回的东西已经引发了渲染,组件已经被挂载到 DOM 树上了。

如果一个父组件中有多个子组件,会按照子组件的顺序依次调用子组件各自的 constructor、componentWillMount、render 函数,等所有子组件的这单个函数都执行完了之后,才会执行第一个子组件的 componentDidMount 函数。

componentWillMount 函数都是紧贴自己的 render 函数之前调用,componentDidMount 函数是当所有子组件的 render 函数都调用了之后,子组件的 componentDidMount 函数(顺序)一起被调用。

更新过程

组件能够随着用户操作改变展示的内容,提供更好的用户体验。当 props 或者 state 被修改时,就会引发组件的更新过程。

更新过程调用的生命周期函数:

  • componentWilReceiveProps(nextProps)

    只要父组件的 render 函数被调用,在 render 函数里被渲染的子组件就会经历更新过程,不管父组件传给子组件的 props 有没有改变,都会触发子组件的 componentWillReceiveProps。

    通过this.setState方法触发的更新过程不会调用这个函数。每个组件都可以通过this.forceUpdate函数强行引发一次重绘。

  • shouldComponentUpdate(nextProps, nextState)

    shouldComponentUpdate 这个函数决定了一个组件什么时候不需要渲染。

  • componentWillUpdate

    如果 shouldComponentUpdate 函数返回 true, React 接下来就会依次执行 ComponentWillUpdate、render、ComponentDidUpdate 函数。

  • render

  • componentDidUpdate

    componentDidUpdate 函数无论更新过程发生在服务器端还是浏览器端,该函数都会被调用。

卸载过程

React 组件的卸载过程只涉及一个函数 componentWillUnmount 当组件要从 DOM 树上删除之前,对应的 componentWillUnmount 函数会被调用。

componentWillUnmount 中的工作往往和 componentDidMount 有关。比如在 componentDidMount 中用非 React 的方法创造了一些 DOM 元素,如果撒手不管可能会造成内存泄漏,那就需要在 componentWillUnmount 中把这些创造的 DOM 元素清理掉。

React 组件的生命周期

初始化阶段

  • getDefaultProps:获取实例的默认属性
  • getInitialState:获取每个实例的初始化状态
  • componentWillMount:组件即将被装载、渲染到页面上
  • render:组件在这里生成虚拟的 DOM 节点
  • omponentDidMount:组件真正在被装载之后

运行中状态

  • componentWillReceiveProps:组件将要接收到属性的时候调用
  • shouldComponentUpdate:组件接受到新属性或者新状态的时候(可以返回 false,接收数据后不更新,阻止render调用,后面的函数不会被继续执行了)
  • componentWillUpdate:组件即将更新不能修改属性和状态
  • render:组件重新描绘
  • componentDidUpdate:组件已经更新

销毁阶段

  • componentWillUnmount:组件即将销毁

V16 生命周期函数用法建议

class ExampleComponent extends React.Component {
  // 用于初始化 state
  constructor() {}
  // 用于替换 `componentWillReceiveProps` ,该函数会在初始化和 `update` 时被调用
  // 因为该函数是静态函数,所以取不到 `this`
  // 如果需要对比 `prevProps` 需要单独在 `state` 中维护
  static getDerivedStateFromProps(nextProps, prevState) {}
  // 判断是否需要更新组件,多用于组件性能优化
  shouldComponentUpdate(nextProps, nextState) {}
  // 组件挂载后调用
  // 可以在该函数中进行请求或者订阅
  componentDidMount() {}
  // 用于获得最新的 DOM 数据
  getSnapshotBeforeUpdate() {}
  // 组件即将销毁
  // 可以在此处移除订阅,定时器等等
  componentWillUnmount() {}
  // 组件销毁后调用
  componentDidUnMount() {}
  // 组件更新后调用
  componentDidUpdate() {}
  // 渲染组件函数
  render() {}
  // 以下函数不建议使用
  UNSAFE_componentWillMount() {}
  UNSAFE_componentWillUpdate(nextProps, nextState) {}
  UNSAFE_componentWillReceiveProps(nextProps) {}
}

react 生命周期中,最适合与服务端进行数据交互的是哪个函数

componentDidMount:在这个阶段,实例和 dom 已经挂载完成,可以进行相关的 dom 操作。主要原因有下

  • React 下一代调和算法 Fiber 会通过开始或停止渲染的方式优化应用性能,其会影响到 componentWillMount 的触发次数。对于 componentWillMount 这个生命周期函数的调用次数会变得不确定,React 可能会多次频繁调用 componentWillMount。如果我们将 AJAX 请求放到 componentWillMount 函数中,那么显而易见其会被触发多次,自然也就不是好的选择。
  • 如果我们将 AJAX 请求放置在生命周期的其他函数中,我们并不能保证请求仅在组件挂载完毕后才会要求响应。如果我们的数据请求在组件挂载之前就完成,并且调用了 setState 函数将数据添加到组件状态中,对于未挂载的组件则会报错。而在 componentDidMount 函数中进行 AJAX 请求则能有效避免这个问题

在 React v16 中,哪些生命周期方法将被弃用?

以下生命周期方法将成为不安全的编码实践,并且在异步渲染方面会更有问题。

componentWillMount()componentWillReceiveProps()componentWillUpdate()

从 React v16.3 开始,这些方法使用 UNSAFE_ 前缀作为别名,未加前缀的版本将在 React v17 中被移除。

生命周期方法 getSnapshotBeforeUpdate() 的目的是什么?

新的 getSnapshotBeforeUpdate() 生命周期方法在 DOM 更新之前被调用。此方法的返回值将作为第三个参数传递给componentDidUpdate()

class MyComponent extends React.Component {
  getSnapshotBeforeUpdate(prevProps, prevState) {
    // ...
  }
}

此生命周期方法与 componentDidUpdate() 一起涵盖了 componentWillUpdate() 的所有用例。

如果在初始状态中使用 props 属性会发生什么?

如果在不刷新组件的情况下更改组件上的属性,则不会显示新的属性值,因为构造函数函数永远不会更新组件的当前状态。只有在首次创建组件时才会用 props 属性初始化状态。

以下组件将不显示更新的输入值:

class MyComponent extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      records: [],
      inputValue: this.props.inputValue,
    };
  }

  render() {
    return <div>{this.state.inputValue}</div>;
  }
}

在 render 方法使用使用 props 将会显示更新的值:

class MyComponent extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      record: [],
    };
  }

  render() {
    return <div>{this.props.inputValue}</div>;
  }
}
Last Updated:
Contributors: yiliang114